﻿Imports System.Reflection
Imports System.Text.RegularExpressions


Namespace ObjectsMethods
    Public NotInheritable Class ObjectFactory

        ' Private constructor to prevent instantiation
        Private Sub New()
        End Sub

        ''' <summary>
        ''' This will search the current Application Domain for any class that 
        ''' Implements the Interface of T and the will create an instance of 
        ''' that class and put it in an ObservableCollection
        ''' </summary>
        ''' <typeparam name="T">Some Interface</typeparam>
        ''' <returns>ObservableCollection of classes that implement T</returns>
        ''' <remarks>All classes must have a parameterless constructor</remarks>
        Public Shared Function CreateAListOfInstancesOfAllClassesInTheCurrentAppDomainThatImplementsThisInterface(Of T)() As Collections.ObjectModel.ObservableCollection(Of T)
            Dim baseType As Type = GetType(T)
            Dim returning As New Collections.ObjectModel.ObservableCollection(Of T)
            If baseType.IsInterface Then
                ' Search the all current loaded assemblies for any class that implements T, Create an instance and then add it to the list
                Dim tsk = System.Threading.Tasks.Task.Factory.StartNew(Sub() AppDomain.CurrentDomain.GetAssemblies().SelectMany(Function(s) s.GetTypes()).Where(Function(p) baseType.IsAssignableFrom(p) And Not p.IsAbstract).ToList.ForEach(Sub(intType) returning.Add(DirectCast(Create(intType), T))
                ))
                tsk.Wait()
            End If
            Return returning
        End Function
  

        Public Shared Function Create(ByVal type As Type) As Object

            ' get the default constructor and instantiate
            Dim types(-1) As Type
            Dim info As ConstructorInfo = type.GetConstructor(types)
            Try
                Dim targetObject As Object = info.Invoke(Nothing)
                If targetObject Is Nothing Then
                    Throw New ArgumentException("Can't instantiate type " + type.Name)
                End If

                Return targetObject
            Catch ex As Exception
                Return Nothing
            End Try

        End Function

        Public Shared Function Create(ByVal typeName As String) As Object

            ' resolve the type
            Dim targetType As Type = ResolveType(typeName)
            If targetType Is Nothing Then
                Throw New ArgumentException("Can't load type " + typeName)
            End If

            ' get the default constructor and instantiate
            Dim types(-1) As Type
            Dim info As ConstructorInfo = targetType.GetConstructor(types)
            Dim targetObject As Object = info.Invoke(Nothing)
            If targetObject Is Nothing Then
                Throw New ArgumentException("Can't instantiate type " + typeName)
            End If

            Return targetObject
        End Function

        Public Shared Function ResolveType(ByVal typeString As String) As Type

            Dim commaIndex As Int32 = typeString.IndexOf(",")
            Dim returning As Type
            If typeString.Contains("`1[") Then
                Dim typeIndex As Integer = typeString.LastIndexOf("`1[") + 3
                Dim itemIndex As Integer = typeString.Length - typeIndex - 1
                Dim underType As String = typeString.Substring(typeIndex, itemIndex)
                For Each targetAssembly As Assembly In AppDomain.CurrentDomain.GetAssemblies()
                    Try

                        returning = targetAssembly.GetType(underType, False, False)
                        If returning IsNot Nothing Then
                            Return returning
                        End If
                    Catch ex As Exception

                    End Try
                Next
                Return Nothing

            ElseIf commaIndex < 1 Then
                For Each targetAssembly As Assembly In AppDomain.CurrentDomain.GetAssemblies()
                    Try

                        returning = targetAssembly.GetType(typeString, False, False)
                        If returning IsNot Nothing Then
                            Return returning
                        End If
                    Catch ex As Exception

                    End Try
                Next
                Return Nothing

            Else


                Dim targetAssembly As Assembly = Nothing
                Dim className As String = typeString.Substring(0, commaIndex).Trim()

                Dim assemblyName As String = typeString.Substring(commaIndex + 1).Trim()

                ' Get the assembly containing the handler

                Try
                    Return Type.GetType(typeString)
                    targetAssembly = Assembly.Load(assemblyName)
                Catch
                    If typeString.Contains("[[") Then
                        Return Type.GetType(typeString)
                    End If
                    Try
                        targetAssembly = [Assembly].Load(assemblyName)
                    Catch
                        Throw New ArgumentException("Can't load assembly " + assemblyName)
                    End Try
                End Try

                ' Get the type
                Return targetAssembly.GetType(className, False, False)
            End If

        End Function
        Public Shared Function GetListType(typeString As String) As String
            Const REGEXSTRING As String = "^.+?\[\[(?<UnderLingType>RBS\.DataObjects\.TableDefinition,\ RBS\.Core,\ Version=1\.0\.294\.221,\ Culture=neutral,\ PublicKeyToken=null)]]$"
            Dim options As RegexOptions = RegexOptions.Multiline

            Dim underlying As String = ""
            Dim matches As MatchCollection = Regex.Matches(typeString, REGEXSTRING, options)
            For Each match As Match In matches
                underlying = match.Groups("UnderLingType").Value
            Next
            Return underlying
        End Function
    End Class

End Namespace